package io.gitee.zhangbinhub.acp.boot.websocket.servlet

import io.gitee.zhangbinhub.acp.boot.component.BootLogAdapter
import io.gitee.zhangbinhub.acp.boot.log.LogAdapter
import io.gitee.zhangbinhub.acp.boot.tools.HttpTools
import io.gitee.zhangbinhub.acp.boot.tools.IpTools.getRemoteIP
import org.springframework.util.LinkedMultiValueMap
import org.springframework.util.MultiValueMap
import org.springframework.web.socket.CloseStatus
import org.springframework.web.socket.WebSocketSession
import java.nio.ByteBuffer
import java.util.concurrent.ConcurrentHashMap

abstract class AcpWebSocketServerServlet(
    logAdapter: LogAdapter,
    private val clientIdKey: String? = null,
    private val clientIdIndex: Int? = null
) : AcpWebSocketHandlerServlet(logAdapter) {
    @JvmField
    var clientId: String? = null

    @JvmField
    var clientIp: String? = null

    @JvmField
    var queryMap: MultiValueMap<String, String> = LinkedMultiValueMap()
    abstract fun getPath(): String

    fun close() {
        session?.close()
    }

    override fun afterConnectionEstablished(session: WebSocketSession) {
        this.session = session
        this.queryMap = HttpTools.getQueryMap(session.uri)
        if (clientIdIndex != null) {
            if (this.clientId == null) {
                this.clientId = HttpTools.getPathVariable(session.uri, clientIdIndex)
            }
        }
        if (clientIdKey != null) {
            if (this.clientId == null) {
                this.clientId = queryMap.getFirst(clientIdKey)
            }
            if (this.clientId == null) {
                this.clientId = session.handshakeHeaders[clientIdKey]?.first()
            }
        }
        if (this.clientId == null) {
            logAdapter.error("websocket client id is null")
            session.close()
        } else {
            acpWebSocketInstanceMap[clientId!!] = this
            this.clientIp = getRemoteIP(session)
            sessionOpen(session)
        }
    }

    override fun afterConnectionClosed(session: WebSocketSession, closeStatus: CloseStatus) {
        acpWebSocketInstanceMap.remove(clientId)
        sessionClose(session, closeStatus)
    }

    companion object {
        private val log: LogAdapter = BootLogAdapter()
        private val acpWebSocketInstanceMap: ConcurrentHashMap<String, AcpWebSocketServerServlet> =
            ConcurrentHashMap<String, AcpWebSocketServerServlet>()

        @JvmStatic
        fun getInstance(clientId: String): AcpWebSocketServerServlet? = acpWebSocketInstanceMap[clientId]

        @JvmStatic
        fun getInstanceList(): List<AcpWebSocketServerServlet> =
            acpWebSocketInstanceMap.map { entry -> entry.value }.apply {
                if (this.isEmpty()) {
                    log.warn("WebSocket does not have any clients!")
                }
            }

        /**
         * 主动发送消息到客户端
         * @param message 消息
         * @param clientId 客户端唯一标识
         */
        @JvmStatic
        fun sendMessageToClient(message: String, clientId: String): Boolean = getInstance(clientId).let { instance ->
            if (instance != null) {
                instance.sendMessage(message)
            } else {
                log.error("WebSocket client[$clientId] is not found!")
                false
            }
        }

        /**
         * 主动发送消息到客户端
         * @param message 消息
         * @param clientId 客户端唯一标识
         */
        @JvmStatic
        fun sendMessageToClient(message: ByteBuffer, clientId: String): Boolean =
            getInstance(clientId).let { instance ->
                if (instance != null) {
                    instance.sendMessage(message)
                } else {
                    log.error("WebSocket client[$clientId] is not found!")
                    false
                }
            }

        /**
         * 主动发送消息到客户端
         * @param message 消息
         */
        @JvmStatic
        fun sendMessageToClient(message: String) {
            getInstanceList().forEach { instance ->
                if (!instance.sendMessage(message)) {
                    log.error("WebSocket client[${instance.clientId}] send message failed!")
                }
            }
        }

        /**
         * 主动发送消息到客户端
         * @param message 消息
         */
        @JvmStatic
        fun sendMessageToClient(message: ByteBuffer) {
            getInstanceList().forEach { instance ->
                if (!instance.sendMessage(message)) {
                    log.error("WebSocket client[${instance.clientId}] send message failed!")
                }
            }
        }
    }
}